package com.ladlee.security;

import com.ladlee.config.ClassPathTldsLoader;
import com.ladlee.mobile.SmsAuthenticationSecurityConfig;
import com.ladlee.properties.SecurityConstants;
import com.ladlee.properties.SecurityProperties;
import com.ladlee.validata.config.ValidateCodeSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;

import javax.sql.DataSource;

/**
 * @Auther: dingchang
 * @Date: 2018/12/21 11:42
 * @Description:
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class LadLeeSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 基本配置
     */
    @Autowired
    SecurityProperties securityProperties;

    /**
     * 验证码配置
     */
    @Autowired
    ValidateCodeSecurityConfig validateCodeSecurityConfig;

    /**
     * 短信配置
     */
    @Autowired
    SmsAuthenticationSecurityConfig smsAuthenticationSecurityConfig;

    /**
     * 登陆期间请求成功处理器
     */
    @Autowired
    AuthenticationSuccessHandler loginSuccessHandler;

    /**
     * 登陆期间请求失败处理器
     */
    @Autowired
    AuthenticationFailureHandler loginFailHandler;

    /**
     * security用户验证接口类
     */
    @Autowired
    UserDetailsService userDetailsService;

    /**
     * session信息过期策略
     */
    @Autowired
    SessionInformationExpiredStrategy sessionInformationExpiredStrategy;

    /**
     * session失效策略
     */
    @Autowired
    InvalidSessionStrategy invalidSessionStrategy;

    /**
     * 数据源
     */
    @Autowired
    DataSource dataSource;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers().frameOptions().disable().and()//允许spring security登陆后，跳转到嵌套页面
                .formLogin()
                .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)  //自定义登陆页面配置
                .loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)//处理表单中自定义的登录URL
                .successHandler(loginSuccessHandler) //登陆成功处理器 返回JSON
                .failureHandler(loginFailHandler) //登录失败处理器
                .and()
                .apply(validateCodeSecurityConfig)  //加入验证码配置
                .and()
                .apply(smsAuthenticationSecurityConfig) //加入短信配置
                .and()
                .rememberMe()
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(securityProperties.getRememberMeSeconds())
                .userDetailsService(userDetailsService) // 登陆验证用户的接口类，重写了默认的UserDetailsService
                .and()
                .sessionManagement()
                //.sessionAuthenticationErrorUrl("/session/invalid") //####
                //.enableSessionUrlRewriting(true) //####
                .invalidSessionStrategy(invalidSessionStrategy) //session失效策略
                .invalidSessionUrl("/session/invalid")//session失效跳转的地址
                .maximumSessions(securityProperties.getSession().getMaximumSessions()) //最大session并发数量
                .maxSessionsPreventsLogin(securityProperties.getSession().isMaxSessionsPreventsLogin()) //之后的登录踢掉之前的登录
                .expiredSessionStrategy(sessionInformationExpiredStrategy) //session过期策略
                .and() //SessionManagementConfigurer<HttpSecurity>
                .and() //HttpSecurity
                .logout() //LogoutConfigurer<HttpSecurity>
                //.logoutSuccessHandler("自己定义登出处理");//
                .logoutUrl("/signOut") //登出的地址
                .logoutSuccessUrl("/") //登出之后跳转的地址
                .deleteCookies("JSESSIONID") //删除cooik
                .and()
                .authorizeRequests().antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
                        SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM,
                        SecurityConstants.DEFAULT_SIGN_IN_URL_MOBILE_PAGE,
                        "/**/*.js","/**/*.css","/**/*.jpg","/**/*.png","/**/*.woff2","/code/*","/session/invalid").permitAll() //以上所有请求都不需要验证
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();
    }

    /**
     * 记住我token的储存
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }

    /**
     * 登陆的时候验证加密的密码
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * security密码加密算法
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(PasswordEncoder.class)
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 加载security的全权限标签
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(ClassPathTldsLoader.class)
    public ClassPathTldsLoader classPathTldsLoader(){
        return new ClassPathTldsLoader();
    }

    /**
     * 去除角色中role_的前缀
     * 表达式需要.access("hasRole('ADMIN')");
     *
     * @throws Exception
     */
    @Bean
    public GrantedAuthorityDefaults grantedAuthorityDefaults() {
        return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}
